#include <dim-sum/beehive.h>
#include <dim-sum/blk_infrast.h>
#include <dim-sum/err.h>
#include <dim-sum/fnode.h>
#include <dim-sum/magic.h>
#include <dim-sum/mount.h>
#include <dim-sum/pagemap.h>
#include <dim-sum/sched.h>
#include <dim-sum/smp_rwlock.h>
#include <dim-sum/stacktrace.h>
#include <dim-sum/syscall.h>
#include <dim-sum/fs.h>

#include "romfs.h"

static const char *root_path = "/";

static struct romfs_file_node *root_node;

static struct smp_lock romfs_lock = SMP_LOCK_UNLOCKED(romfs_lock);

static unsigned long fnode_num = 1;

static struct file_node *romfs_creat_fnode(struct super_block *super,
	struct romfs_file_node *node, struct filenode_cache *fnode_cache);

/**
 * cpio image info from linker
 */
extern char _cpio_start[];
extern char _cpio_end[];

static unsigned long cpio_image_sz = 0;

static unsigned long cpio_blk_read(void *buf, long off, unsigned long len)
{
    memcpy((void *)buf, (const void *)(_cpio_start + off), len);
	return len;
}

static unsigned long cpio_blk_get_capacity(void)
{
    return cpio_image_sz;
}

static void cpio_blk_init(void)
{
    cpio_image_sz = (unsigned long)(_cpio_end - _cpio_start);
}

static int cpio_check_valid(void)
{
	struct cpio_newc_header header;
	unsigned long rd;

	if(cpio_blk_get_capacity() <= sizeof(struct cpio_newc_header))
		return -1;

	rd = cpio_blk_read((unsigned char *)(&header), 0, sizeof(struct cpio_newc_header));
	if(rd != sizeof(struct cpio_newc_header))
		return -1;

	if(strncmp((const char *)header.c_magic, "070701", 6) != 0)
		return -1;

	return 0;
}

static bool get_next_token(const char * path, const char * prefix, char * result)
{
	const char * p, * q;
	int l;

	if(!path || !prefix || !result)
		return false;

	if(*path == '/')
		path++;

	if(*prefix == '/')
		prefix++;

	l = strlen(prefix);
	if(strncmp(path, prefix, l) != 0)
		return false;

	p = &path[l];
	if(*p == '\0')
		return false;
	if(*p == '/')
		p++;
	if(*p == '\0')
		return false;

	q = strchr(p, '/');
	if(q)
	{
		if (*(q + 1) != '\0')
			return false;
		l = q - p;
	}
	else
	{
		l = strlen(p);
	}
	memcpy(result, p, l);
	result[l] = '\0';

	return true;
}

static bool check_path(const char * path, const char * prefix, const char * name)
{
	int l;

	if(!path || !prefix || !name)
		return false;

	if(path[0] == '/')
		path++;

	if(prefix[0] == '/')
		prefix++;

	l = strlen(prefix);
	if(l && (strncmp(path, prefix, l) != 0))
		return false;

	path += l;

	if(path[0] == '/')
		path++;

	if(strcmp(path, name) != 0)
		return false;

	return true;
}

static struct romfs_file_node *fnode_to_romfs(struct file_node *fnode)
{
	if (fnode == NULL)
		return NULL;

	return fnode->private;
}

static struct romfs_file_node *
hold_romfs_node(struct romfs_file_node *node)
{
	if (node)
		accurate_inc(&node->ref_count);

	return node;
}

static void
loosen_romfs_node(struct romfs_file_node *node)
{
	if (!node)
		return;

	if (!accurate_dec_and_test_zero(&node->ref_count))
		return;

	if (node == root_node)
		panic("romfs: try to free root node.\n");

	if (node->rel_path != root_path)
		kfree(node->rel_path);

	kfree(node);
}

static struct romfs_file_node *
romfs_alloc(const char *name, unsigned int len, umode_t mode)
{
	struct romfs_file_node *ret;

	if (name && len <= 0)
		len = strlen(name);

	ret = kmalloc(sizeof(*ret) + len + 1, PAF_KERNEL | __PAF_ZERO);
	if (!ret)
		return NULL;

	accurate_set(&ret->ref_count, 1);
	ret->mode = mode;
	ret->file_size = 0;
	/**
	 * 默认cpio文件路径为根目录
	 */
	ret->rel_path = (char*)root_path;
	ret->priv = NULL;
	ret->name_len = len;
	if (name)
		memcpy(ret->name, name, len);
	smp_lock(&romfs_lock);
	fnode_num++;
	ret->node_num = fnode_num;
	smp_unlock(&romfs_lock);

	return ret;
}

/**
 * 单实例分配根节点
 */
static struct romfs_file_node *romfs_alloc_root(void)
{
	struct romfs_file_node *node;

	if (root_node)
		return root_node;

	node = romfs_alloc(NULL, 0, MODE_DIR);
	if (!node) {
		panic("romfs: fail to alloc root node.\n");
		return NULL;
	}

	smp_lock(&romfs_lock);
	if (root_node) {
		smp_unlock(&romfs_lock);
		loosen_romfs_node(node);
		return root_node;
	}
	root_node = node;
	smp_unlock(&romfs_lock);

	return root_node;
}

static int romfs_free_root(struct romfs_file_node *node)
{
	if (!node)
		return -1;

	if (root_node != node)
		return -1;

	smp_lock(&romfs_lock);
	
	root_node = NULL;

	loosen_romfs_node(node);
	smp_unlock(&romfs_lock);

	return 0;
}

static int cache_ops_may_delete(struct filenode_cache *fnode_cache)
{
	return !!fnode_cache->file_node;
}

static void cache_ops_loosen(struct filenode_cache *fnode_cache,
	struct file_node *fnode)
{
	struct romfs_file_node *node;

	node = fnode_to_romfs(fnode);

	if (node->fnode_cache && (node->fnode_cache != fnode_cache))
		panic("(%s): node: %p filenode_cache: %p node->filenode_cache: %p\n",
		     node->name, node, fnode_cache, node->fnode_cache);

	node->fnode_cache = NULL;
	loosen_file_node(fnode);
	loosen_romfs_node(node);
}

static struct fnode_cache_ops romfs_fnode_cache_ops = {
	.may_delete = cache_ops_may_delete,
	.loosen_file_node = cache_ops_loosen,
};

static int cpio_lookup(struct romfs_file_node *dn, const char * name, struct romfs_file_node *n)
{
	struct cpio_newc_header header;
	char path[ROMFS_PATH_MAX];
	unsigned long off = 0, rd;
	unsigned int size, name_size, mode, mtime;
	unsigned char buf[9];

	while(1) {
		rd = cpio_blk_read((unsigned char *)&header, off, sizeof(struct cpio_newc_header));
		if(rd != sizeof(struct cpio_newc_header))
			return -1;

		if(strncmp((const char *)header.c_magic, "070701", 6) != 0)
			return -1;

		buf[8] = '\0';

		memcpy(buf, &header.c_filesize, 8);
		size = simple_strtoull((const char *)buf, NULL, 16);

		memcpy(buf, &header.c_namesize, 8);
		name_size = simple_strtoull((const char *)buf, NULL, 16);

		memcpy(buf, &header.c_mode, 8);
		mode = simple_strtoull((const char *)buf, NULL, 16);

		memcpy(buf, &header.c_mtime, 8);
		mtime = simple_strtoull((const char *)buf, NULL, 16);

		rd = cpio_blk_read((unsigned char *)path, off + sizeof(struct cpio_newc_header), name_size);
		if(!rd)
			return -1;

		if((size == 0) && (mode == 0) && (name_size == 11) && (strncmp(path, "TRAILER!!!", 10) == 0))
			return -1;

		if((path[0] != '.') && check_path(path, dn->rel_path, name))
			break;

		off += sizeof(struct cpio_newc_header);
		off += (((name_size + 1) & ~3) + 2) + size;
		off = (off + 3) & ~0x3;
	}
	/**
	 * 将时间戳转换成timespace
	 */
	n->access_time.tv_sec = le32_to_cpu(mtime);
	n->meta_modify_time.tv_sec = le32_to_cpu(mtime);
	n->data_modify_time.tv_sec = le32_to_cpu(mtime);
	n->access_time.tv_nsec = n->meta_modify_time.tv_nsec = n->data_modify_time.tv_nsec = 0;

	n->mode = mode;
	n->file_size = size;
	n->name_len = name_size;
	memcpy(n->name, name, name_size);

	off += sizeof(struct cpio_newc_header);
	off += (((name_size + 1) & ~3) + 2);
	n->priv = (void *)((unsigned long)off);

	return 0;
}

/**
 * 根据文件名，在目录中搜索指定的文件
 */
static struct romfs_file_node *__find_in_dir(struct romfs_file_node *dir,
					     const char *name,
					     unsigned int len)
{
	struct romfs_file_node *node;

	if (!S_ISDIR(dir->mode)) {
		pr_err("(%s): not a directory\n", dir->name);
		return NULL;
	}

	node = romfs_alloc(NULL, len, 0);
	if (!node)
		return NULL;

	if (cpio_lookup(dir, name, node)) {
		loosen_romfs_node(node);
		return NULL;
	}

	/**
	 * 如果节点是目录，则根据父节点构造cpio绝对路径
	 */
	if (S_ISDIR(node->mode)) {
		size_t n = strlen(dir->rel_path);
		/**
		 * 分配节点的路径长度: 目录路径长度 + '/' + 节点名字长度 + '\0'
		 */
		node->rel_path = kmalloc(n + 1 + len + 1, PAF_KERNEL | __PAF_ZERO);
		if (!node->rel_path) {
			loosen_romfs_node(node);
			return NULL;
		}

		strcpy(node->rel_path, dir->rel_path);
		if (node->rel_path[n - 1] != '/')
			strcat(node->rel_path, "/");

		strcat(node->rel_path, name);
	}

	return node;
}

static struct filenode_cache *
romfs_lookup(struct file_node *dir, struct filenode_cache *fnode_cache,
	struct filenode_lookup_s *look)
{
	struct romfs_file_node *dir_node, *node;
	struct filenode_cache *ret = NULL;
	struct file_node *fnode;

	dir_node = fnode_to_romfs(dir);

	node = __find_in_dir(dir_node, fnode_cache->file_name.name,
			       fnode_cache->file_name.len);

	if (!node)
		goto out;

	/**
	 * 文件节点存在，为它创建VFS层节点描述符
	 */
	fnode = romfs_creat_fnode(dir->super, node, fnode_cache);
	if (!fnode) {
		ret = ERR_PTR(-ENOMEM);
		goto out;
	}

	fnode_cache_stick(fnode_cache, fnode);

out:
	fnode_cache->ops = &romfs_fnode_cache_ops;
	loosen_romfs_node(node);

	return ret;
}

static int romfs_sync_file(struct file *file, struct filenode_cache *filenode_cache, int datasync)
{
	return 0;
}

static unsigned long cpio_read(struct romfs_file_node *node, long off, const void * buf, unsigned long len)
{
	unsigned long toff;
	unsigned long sz = 0;

	/**
	 * 普通文件和链接文件可以被读取
	 */
	if(!S_ISREG(node->mode) && !S_ISLNK(node->mode))
		return 0;

	if(off >= node->file_size)
		return 0;

	sz = len;
	if((node->file_size - off) < sz)
		sz = node->file_size - off;

	toff = (unsigned long)((unsigned long)(node->priv));
	sz = cpio_blk_read((char *)buf, (toff + off), sz);

	return sz;
}

static ssize_t romfs_file_read(struct file *file, char __user *buf,
			   size_t count, loff_t *ppos)
{	
	struct file_node *fnode;
	struct romfs_file_node *rom_node;
	ssize_t n;

	fnode = file->fnode_cache->file_node;
	rom_node = fnode_to_romfs(fnode);

	n = cpio_read(rom_node, *ppos, buf, count);
	*ppos += n;
	return n;
}

static int cpio_readdir(struct romfs_file_node *dir, long off, struct romfs_file_node *n)
{
	struct cpio_newc_header header;
	char path[ROMFS_PATH_MAX];
	char name[NAME_MAX];
	unsigned int size, name_size, mode;
	unsigned long toff = 0, rd;
	char buf[9];
	int i = 0;

	while(1) {
		rd = cpio_blk_read((unsigned char *)&header, toff, sizeof(struct cpio_newc_header));
		if(rd != sizeof(struct cpio_newc_header))
			return -1;

		if(strncmp((const char *)&header.c_magic, "070701", 6) != 0)
			return -1;

		buf[8] = '\0';

		memcpy(buf, &header.c_filesize, 8);
		size = simple_strtoull((const char *)buf, NULL, 16);

		memcpy(buf, &header.c_namesize, 8);
		name_size = simple_strtoull((const char *)buf, NULL, 16);

		memcpy(buf, &header.c_mode, 8);
		mode = simple_strtoull((const char *)buf, NULL, 16);

		rd = cpio_blk_read( (unsigned char *)path, toff + sizeof(struct cpio_newc_header), name_size);
		if(!rd)
			return -1;

		if((size == 0) && (mode == 0) && (name_size == 11) && (strncmp(path, "TRAILER!!!", 10) == 0))
			return -1;

		toff += sizeof(struct cpio_newc_header);
		toff += (((name_size + 1) & ~3) + 2) + size;
		toff = (toff + 3) & ~3;

		if(path[0] == '.')
			continue;

		if(!get_next_token(path, dir->rel_path, name))
			continue;

		if(i++ == off) {
			toff = 0;
			break;
		}
	}

	/**
	 * 更新获取到的node信息
	 */
	n->mode = mode;
	n->file_size = size;
	n->name_len = name_size;
	memcpy(n->name, name, name_size);

	/* get next inode */
	fnode_num++;
	n->node_num = fnode_num;

	return 0;
}

static int romfs_readdir(struct file *file, void *dirent, filldir_t filldir)
{
	struct romfs_file_node *rom_node, *node = NULL;
	struct file_node *fnode;
	int err;
	int stored = 0;

	if ((long)file->pos < 0)
		return -EINVAL;

	fnode = file->fnode_cache->file_node;
	rom_node = fnode_to_romfs(fnode);

	switch ((long)file->pos) {
	case 0:
		err = filldir(dirent, "..", 2, file->pos,
			parent_fnode_num(file->fnode_cache), DT_DIR);

		if (err == -EINVAL)
			break;

		if (err < 0)
			return err;

		file->pos++;
		stored++;
	case 1:
		err = filldir(dirent, ".", 1, file->pos,
			fnode->node_num, DT_DIR);

		if (err == -EINVAL)
			break;

		if (err < 0)
			return err;

		file->pos++;
		stored++;
	default:
		/**
		 * 创建一个新的rom节点来保存读取到的文件信息
		 */
		node = romfs_alloc(NULL, NAME_MAX, 0);
		if (!node)
			return -ENOMEM;

		/**
		 * 遍历读取cpio文件节点，基础偏移是0，因此需要将pos-2.
		 */
		while (!cpio_readdir(rom_node, file->pos - 2, node)) {
			err = (*filldir) (dirent, node->name, node->name_len,
					  file->pos, node->node_num, node->mode >> 12);
			if (err < 0)
				loosen_romfs_node(node);
			else {
				file->pos++;
				stored++;
			}

			if (err == -EINVAL)
				break;

			if (err < 0)
				return err;
			
			memset(node->name, 0, NAME_MAX);
		}

		loosen_romfs_node(node);

		break;
	}

	return stored;
}

static int romfs_statfs(struct super_block *sb, struct kstatfs *buf)
{
	buf->f_type = sb->magic;
	buf->f_bsize = CPIO_BLK_SIZE;
	buf->f_namelen = NAME_MAX;
	return 0;
}

/**
 * 将符号链接的内容读取到内存中
 * 读取节点的数据内容，并保存到synlink符号里面。
 */
static int romfs_follow_link(struct filenode_cache *fnode_cache,
	struct filenode_lookup_s *look)
{
	struct romfs_file_node *rom_node = fnode_to_romfs(fnode_cache->file_node);
	if (rom_node) {
		/**
		 * 从cpio中读取文件内容，并将内容作为链接名字保存。
		 */
		char *sym_name = kmalloc(rom_node->file_size + 1, PAF_KERNEL | __PAF_ZERO);
		if (!sym_name) {
			pr_err("(%s) romfs alloc symlink name failed! no enough memory!\n", __func__);
			return -ENOMEM;
		}
		
		if (!cpio_read(rom_node, 0, sym_name, rom_node->file_size)) {
			pr_err("(%s) read symlink file failed!\n", __func__);
			kfree(sym_name);
			return -EIO;
		}

		look_save_symlink_name(look, sym_name);
	}
	else
		look_save_symlink_name(look, ERR_PTR(-ENODEV));

	return 0;
}

static void romfs_loosen_link(struct filenode_cache *fnode_cache, struct filenode_lookup_s *look)
{
	if (!IS_ERR(look_curr_symlink_name(look))) {
		/**
		 * 释放符号名
		 */
		kfree(look_curr_symlink_name(look));
	}
}

/**
 * 通常的符号链接文件回调表
 */
struct file_node_ops romfs_symlink_fnode_ops = {
	.read_link	= generic_read_link,
	.follow_link	= romfs_follow_link,
	.loosen_link	= romfs_loosen_link,
};

static struct super_block_ops romfs_super_ops = {
	.statfs		= romfs_statfs,
	.release	= generic_delete_fnode,
};

static struct file_ops romfs_file_ops = {
	.read		= romfs_file_read,
	.fsync		= romfs_sync_file,
	.llseek		= generic_file_llseek,
};

static struct file_node_ops romfs_file_fnode_ops = {
	.get_attribute	= simple_getattr,
};

static struct file_node_ops romfs_dir_fnode_ops = {
	.lookup		= romfs_lookup,
};

struct file_ops romfs_dir_ops = {
	.read		= generic_read_dir,
	.readdir	= romfs_readdir,
};

static struct file_node *
romfs_creat_fnode(struct super_block *super, struct romfs_file_node *node,
	struct filenode_cache *fnode_cache)
{
	struct file_node *fnode;

	/**
	 * 分配一个VFS文件节点
	 */
	fnode = fnode_alloc(super);
	if (!fnode) {
		pr_err("(%s): failed to alloc file node, node: %p\n", node->name, node);
		return NULL;
	}

	node->fnode_cache = fnode_cache;

	fnode->private = hold_romfs_node(node);
	fnode->node_num = node->node_num;
	fnode->block_count = 0;
	fnode->block_size = CPIO_BLK_SIZE;
	fnode->mode = node->mode;
	
	switch (node->mode & S_IFMT) {
		default:
			pr_err("(%s): unknown mode %o node: %p\n",
		       node->name, node->mode, node);
			loosen_file_node(fnode);
			loosen_romfs_node(node);
			return NULL;
			break;
		case S_IFREG:
			fnode->node_ops = &romfs_file_fnode_ops;
			fnode->file_ops = &romfs_file_ops;
			break;
		case S_IFDIR:
			fnode->node_ops = &romfs_dir_fnode_ops;
			fnode->file_ops = &romfs_dir_ops;
			break;
		case S_IFLNK:
			fnode->node_ops = &romfs_symlink_fnode_ops;
			break;
		}
	fnode->uid = node->uid;
	fnode->gid = node->gid;
	fnode->access_time = node->access_time;
	fnode->data_modify_time = node->data_modify_time;
	fnode->meta_modify_time = node->meta_modify_time;
	fnode->file_size = node->file_size;

	return fnode;
}

static int romfs_fill_super(struct super_block *super, void *data, int silent)
{
	struct file_node *fnode = NULL;
	struct romfs_file_node *root;

	/**
	 * 检查cpio挂载信息
	 */
	if (cpio_check_valid()) {
		pr_err("romfs: invalid cpio image !\n");
		goto fail;
	}

	root = romfs_alloc_root();
	if (!root)
		goto fail;

	super->fs_info = NULL;
	super->block_size = CPIO_BLK_SIZE;
	super->block_size_order = CPIO_BLK_SIZE_ORDER;
	super->magic = ROMFS_MAGIC;
	super->ops = &romfs_super_ops;
	super->gran_ns = 1;

	fnode = romfs_creat_fnode(super, root, NULL);
	if (NULL == fnode)
		goto fail_root;

	super->root_fnode_cache = fnode_cache_alloc_root(fnode);
	if (!super->root_fnode_cache)
		goto fail_fnode;

	return 0;

fail_fnode:
	loosen_file_node(fnode);
fail_root:
	romfs_free_root(root);
	pr_err("romfs: fail to fill super_block.\n");
fail:
	return -EINVAL;
}

/**
 * 初始根文件系统的get_sb。
 */
static struct super_block *
romfs_load_filesystem(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data)
{
	return load_single_filesystem(fs_type, flags, data, romfs_fill_super);
}

static struct file_system_type romfs_fs_type = {
	.name		= "romfs",
	.load_filesystem		= romfs_load_filesystem,
	.unload_filesystem	= unload_isolate_filesystem,
};

void __init mount_romfs_fs(void)
{
	int err;

	sys_mkdir("/rom", 0);

	err = do_mount("none", "/rom", "romfs", 0, NULL);
	if (err == 0)
		pr_info("Mounted romfs on /rom\n");
	else
		pr_err("Unable to mount romfs, err: %d\n", err);
}

/**
 * 向内核注册特殊文件系统类型romfs，用于后期挂载初始根目录
 */
int __init init_romfs(void)
{
	int err;

	pr_info("init romfs from cpio\n");

	/**
	 * 初始化CPIO的块设备
	 */
	cpio_blk_init();

	if (romfs_alloc_root() == NULL)
		return -1;

	err = register_filesystem(&romfs_fs_type);

	return err;
}
